{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import sys\n",
    "\n",
    "sys.path.append(\"../../../\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from fedlab.utils.dataset.partition import CIFAR10Partitioner\n",
    "from fedlab.utils.dataset import functional as F\n",
    "from fedlab.utils.functional import partition_report"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Files already downloaded and verified\n"
     ]
    }
   ],
   "source": [
    "trainset = torchvision.datasets.CIFAR10(root=\"../../../../data/CIFAR10/\", train=True, download=True)\n",
    "\n",
    "num_clients = 100\n",
    "num_classes = 10\n",
    "\n",
    "seed = 2021\n",
    "\n",
    "hist_color = '#4169E1'\n",
    "plt.rcParams['figure.facecolor'] = 'white'"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Hetero Dirichlet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n"
     ]
    }
   ],
   "source": [
    "# perform partition\n",
    "hetero_dir_part = CIFAR10Partitioner(trainset.targets, \n",
    "                                num_clients,\n",
    "                                balance=None, \n",
    "                                partition=\"dirichlet\",\n",
    "                                dir_alpha=0.3,\n",
    "                                seed=seed)\n",
    "# save to pkl file\n",
    "torch.save(hetero_dir_part.client_dict, \"cifar10_hetero_dir.pkl\")\n",
    "print(len(hetero_dir_part))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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WK1Wo76+3ti9e7fRp08fo6mpqd1j+uabb4wvv/zSMAzD+PHHH42wsDBj+/btXo3r5MmTxpEjRwzDMIzjx48bAwYMMD7//HOvf1aGYRh/+tOfjAkTJhh33HGHYRje/+934403Gt99912zY96OyTAM47777jNycnIMwzCMhoYG4+DBgz4Rl2Gc+j1w3XXXGRUVFV6NqaamxggJCTHq6uoMwzCMu+66y3jjjTd85nOS9qfk3ILPPvvMGD58uPP7zMxMIzMz84LG8PXXXzdLzn379jW++eYbwzBOJcq+ffueNbbhw4cbn332mcfjGz16tJGfn+8zcdXW1hoxMTHGF1984fWYqqurjcTERGPNmjXO5OztmM6WnL0d0+HDh42QkBDj5MmTPhXXaR9//LHxy1/+0usx1dTUGMHBwcaBAweMxsZG44477jA+/vhjn/mcpP3psnYL9uzZQ69evZzfBwcHs2fPHi9GBPv27SMoKAiAoKAg9u/fD3gn1oqKCkpKShg4cKDX4zpx4gQ2m43u3bszbNgwn4jpkUce4fnnn+eSS/71v5i3Y/Lz82P48OHExsaSnZ3tEzHt3r2bbt268cADDxATE8NDDz1EbW2t1+M6bcmSJUyYMAHw7mfVs2dPZsyYwQ033EBQUBCBgYEMHz7cZz4naX9Kzi0wzvKEmZ+fnxciObcLHevRo0cZM2YM8+bN44orrvB6XP7+/jgcDmpqati0aROlpaVejemDDz6ge/fuxMbGtqn9hfqcNmzYwObNm/nHP/7BK6+8wieffOL1mJqamti8eTNTp06lpKSEX/ziF62u77iQP+vHjx8nLy+Pu+66q9V2FyKmgwcPsnLlSr7++mu++eYbamtreeutt7wak3iWknMLgoODqa6udn5fU1NDjx49vBgRXHfddXz77bcAfPvtt3Tv3h24sLE2NjYyZswYJk6cSEpKis/EBXDllVeSkJDARx995NWYNmzYQF5eHiEhIYwfP561a9dy7733ev1zOn3O7t27k5yczKZNm7weU3BwMMHBwQwcOBCAsWPHsnnzZq/HBfCPf/yD/v37c9111wHe/TlfvXo1vXv3plu3bphMJlJSUvjss8984nMSz1BybkF8fDy7du3i66+/5vjx4yxZsoTRo0d7NabRo0ezePFiABYvXsydd97pPL5kyRIaGhr4+uuv2bVrFwMGDGj38Q3D4MEHHyQiIoI//OEPPhHXd999x6FDhwA4duwYq1evpl+/fl6Nae7cudTU1FBRUcGSJUtITEzkrbfe8mpMtbW1HDlyxPl1fn4+FovF6z9T119/Pb169WLnzp0ArFmzhsjISK/HBfDOO+84L2mfHttbMd1www188cUX1NXVYRgGa9asISIiwic+J/EQL93rviisWrXKCAsLM/r06WPMmTPngo49fvx44/rrrzc6depk9OzZ03jttdeM77//3khMTDRuuukmIzEx0Thw4ICz/Zw5c4w+ffoYffv2NT788EOPxPTpp58agBEVFWVER0cb0dHRxqpVq7wa15YtWwybzWZERUUZZrPZmD17tmEYhtc/q9MKCwudC8K8GVN5eblhtVoNq9VqREZGOn+efeFzKikpMWJjY42oqCjjzjvvNH744Qevx1VbW2tcffXVxqFDh5zHvB3T008/bYSHhxtms9m49957jfr6eq/HJJ6j7TtFRER8jC5ri4iI+BglZxERER+j5CwiIuJjlJxFRER8jJKziIiIj1FyFrnIJCQkUFxc7O0wRMSDlJxFRER8jJKzyHmqra3ljjvuIDo6GovFwtKlSwF49tlniY+Px2KxYLfbnfsdJyQk8OijjzJ48GAiIiIoKioiJSWFsLAwnnrqKeBUYZF+/foxadIkrFYrY8eOpa6u7oyx8/PzueWWW+jfvz933XUXR48ePaNNQkICjz/+OAMGDKBv3758+umnAOTm5vLwww87240aNYp169YB0LVrVx5//HFiY2MZOnQomzZtIiEhgT59+pCXl9eun5+InEnJWeQ8ffTRR/To0YMtW7ZQWlrKiBEjAHj44YcpKiqitLSUY8eO8cEHHzj7XHrppXzyySdMmTKFO++8k1deeYXS0lJyc3M5cOAAADt37sRut7N161auuOIK/vu//7vZuN9//z1z5sxh9erVbN68mbi4OF588cWzxtjU1MSmTZuYN28es2fPPud7qq2tJSEhgS+//JLLL7+cp556ioKCAlasWMHTTz/t7kclIm2k5CxynqKioli9ejWPP/44n376KYGBgQAUFhYycOBAoqKiWLt2Ldu3b3f2Ob1Pe1RUFGazmaCgIAICAujTp4+zYEGvXr249dZbAbj33nv5n//5n2bjfvHFF+zYsYNbb70Vm83G4sWLqaysPGuMp4uUxMbGUlFRcc73dOmllzr/yIiKiuK2227DZDIRFRXVpv4icn46eTsAkYtd3759+fLLL/nwww954oknGD58OLNmzeI//uM/KC4uplevXqSnp1NfX+/sExAQAMAll1zi/Pr0901NTcCZJf7+9/eGYTBs2DDeeeedc8Z4egx/f3/n+Tt16sTJkyedbX4an8lkco730xh/Gp+IeI5mziLn6ZtvvuGyyy7j3nvvZcaMGWzevNmZ6K699lqOHj3KsmXLXD5vVVUVn3/+OXCqQtKvfvWrZq/ffPPNbNiwgf/7f/8vAHV1dZSVlbX5/CEhITgcDk6ePEl1dTWbNm1yOUYR8QzNnEXO07Zt25g5cyaXXHIJJpOJP//5z1x55ZX89re/JSoqipCQEOLj410+b0REBIsXL+Z3v/sdYWFhTJ06tdnr3bp1Izc3lwkTJtDQ0ADAnDlz6Nu3b5vOf+utt9K7d2+ioqKwWCz079/f5RhFxDNUlUrEB1VUVDBq1ChKS0u9HYqIeIEua4uIiPgYzZxFRER8jGbOIiIiPkbJWURExMcoOYuIiPgYJWcREREfo+QsIiLiY/4fWAf2fAVrXWQAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/cifar10_hetero_dir_0.3_100clients.csv\"\n",
    "partition_report(trainset.targets, hetero_dir_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "hetero_dir_part_df = pd.read_csv(csv_file,header=1)\n",
    "hetero_dir_part_df = hetero_dir_part_df.set_index('client')\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "for col in col_names:\n",
    "    hetero_dir_part_df[col] = (hetero_dir_part_df[col] * hetero_dir_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "hetero_dir_part_df[col_names].iloc[:10].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/cifar10_hetero_dir_0.3_100clients.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# plot sample number distribution for clients\n",
    "clt_sample_num_df = hetero_dir_part.client_sample_count\n",
    "sns.histplot(data=clt_sample_num_df, \n",
    "             x=\"num_samples\", \n",
    "             edgecolor='none', \n",
    "             alpha=0.7, \n",
    "             shrink=0.95,\n",
    "             color=hist_color)\n",
    "plt.savefig(f\"../imgs/cifar10_hetero_dir_0.3_100clients_dist.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Shards Partition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n"
     ]
    }
   ],
   "source": [
    "num_shards = 200\n",
    "shards_part = CIFAR10Partitioner(trainset.targets, \n",
    "                                num_clients,\n",
    "                                balance=None, \n",
    "                                partition=\"shards\",\n",
    "                                num_shards=num_shards,\n",
    "                                seed=seed)\n",
    "# # save to pkl file\n",
    "# torch.save(shards_part.client_dict, \"cifar10_shards.pkl\")\n",
    "print(len(shards_part))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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nWHDhtGrVy8SEhJcucQdXknOZrOZZcuWudXGMAyWL19OdHR0h+MbN24kJCTE9To4OJj29vZuxbd48WJWrVrFO++80+ntDRER8S5fPPqUlZVFVlbWad+bPn0606dPP+X4G2+8ccqxhx9+mIcffviU43PnzmXu3LnditErt7VTUlI4fPgwRUVFrmMVFRWsX7/+jG3S0tJYsGABJ8pLV1VVnbUfk8lEW1ubW7G9/fbbPPXUU5SUlHDRRRe51VZERORc8EpyDgoKYsWKFZSVlREZGYnZbCY3N7fTZ71ycnJoa2vDarVisVjIyck5az8OhwOr1erWgrD77ruP/fv3k5qais1m67BoTURExB8EGSemqnJG9rBgKh19fR2GBCDtECa+1N0dwux2O5WVlR2Offzxxx4tkPou6uyz8JsFYX4tLAFyK89+nkgPi8n1dQQi4gvavlNERMTPKDmLiIj4GSVnEREJaD1dMvL555/HYrFgNpt57rnnPLqGvnMWERGfuabc2aPX232zrUev567q6mqKiorYtGkTF154IWPHjuXWW28lKirKreto5iwiIgHFmyUjP/74Y66//nouuugievXqxU033cSKFSvcjlHJWUREAoa3S0ZaLBbeffdd9u3bx8GDB3nrrbdobGx0O07d1hYRkYDRlZKRjz76KM3NzRw4cIC0tDTgXyUjJ02aREZGBnC8ZGReXh5NTU1kZGQQFRVFTEwMDz30EKmpqfTt25f4+HhXJUR3aOYsIiIBw9slIwHuuecePvroI959910uv/xyt79vBiVnEREJIN4uGQmwd+9eABoaGnjjjTe488473Y5Tt7VFRCRgeLtkJMDEiRPZt28fJpOJF154gcsuu8ztOLW3dhdob23xlVzu93UIEsByc3O71V57a3eus89Ct7VFRET8jNeS8+7du8nMzCQyMpLY2FjGjRtHTU0NdXV1WCwWACorK5k5c6bHfeTn57vd5vXXX8dsNnPBBRec8i86ERERf+CV5GwYBunp6SQnJ1NbW8v27dvJz89nz549Hc6z2+3Mnz/f4348Sc4Wi4U33njD9RC5iIiIv/FKci4vL8dkMjFt2jTXMZvNxsiRIzuct27dOsaPHw9AS0sLU6dOJSkpiYSEBFauXAlAcXExGRkZjB07lqioKObMmQNAdnY2hw4dwmazMWXKlC7HFhMTQ3R0dHeH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1dXVjB07FoD77ruPiooKqqurOXToEKtWrXK1ufDCC3n33XeZNm0at912Gy+88ALV1dUUFxezb98+AD755BMcDgdbtmzhkksu4X/+53869Pvll18yb9481qxZw0cffYTdbue3v/3taWNsb29n06ZNPPfcc8ydO/esY2ppaSE5OZkPP/yQiy++mEcffZSysjJWrFjBY4895ulHJSJdpOQs0k1xcXGsWbOGhx56iPfee4/Q0FAAysvLGTFiBHFxcaxdu5Zt27a52pzYpz0uLg6z2Uz//v0JCQlh0KBBruICAwcO5MYbbwTgrrvu4n//93879PvBBx+wfft2brzxRmw2G4sXL6a+vv60MZ4o6JGYmEhdXd1Zx3ThhRe6/pERFxfHTTfdhMlkIi4urkvtRaR7VDJSpJuGDBnChx9+yFtvvcXDDz/MLbfcwpw5c/iv//ovKisrGThwILm5ubS2trrahISEAHDBBRe4fj7xur29HTi1HN+/vzYMg9TUVF599dWzxniij+DgYNf1e/XqxbFjx1znnByfyWRy9XdyjCfHJyLeo5mzSDd99tlnXHTRRdx11108+OCDfPTRR65Ed+WVV3LgwAGWLVvm9nUbGhp4//33AXj11Vf54Q9/2OH966+/ng0bNvB///d/ABw8eJCampouXz8iIgKn08mxY8dobGxk06ZNbscoIt6hmbNIN23dupXZs2dzwQUXYDKZ+P3vf8+ll17Kz3/+c+Li4oiIiCApKcnt68bExLB48WJ+8YtfEBUVxfTp0zu8f9VVV1FcXMydd97J4cOHAZg3bx5Dhgzp0vVvvPFGrrvuOuLi4rBYLAwbNsztGEXEO1SVSsQP1dXVMX78eKqrq30dioj4gG5ri4iI+BnNnEVERPyMZs4iIiJ+RslZRETEzyg5i4iI+BklZxERET+j5CwiIuJn/h+MsiXjGaoLYAAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/cifar10_shards_200_100clients.csv\"\n",
    "partition_report(trainset.targets, shards_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "shards_part_df = pd.read_csv(csv_file,header=1)\n",
    "shards_part_df = shards_part_df.set_index('client')\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "for col in col_names:\n",
    "    shards_part_df[col] = (shards_part_df[col] * shards_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "shards_part_df[col_names].iloc[:10].plot.barh(stacked=True)\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/cifar10_shards_200_100clients.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Balanced IID"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n"
     ]
    }
   ],
   "source": [
    "balance_iid_part = CIFAR10Partitioner(trainset.targets, \n",
    "                                num_clients,\n",
    "                                balance=True, \n",
    "                                partition=\"iid\",\n",
    "                                seed=seed)\n",
    "# # save to pkl file\n",
    "# torch.save(balance_iid_part.client_dict, \"cifar10_balance_iid.pkl\")\n",
    "print(len(balance_iid_part))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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smULI0aMwG63Y7PZ2LZtGz/88AMffvgh999/PwAXXnhhm3VCOkvFWUREeoyTLSPXrFnDxo0beeGFF9q8n5KSQnl5ORs3biQ8PJyXX34ZwNkycuPGjRQXFwP/bBnpcDioqKggKCiI7du3c9VVV/HLX/6S6OhoHnjgAZqamlzOU8VZRER6jM60jBw1ahSRkZG8/vrrbNmyBfhny8iCggJn3+YbbriB3Nxc5s2bR11dHX369KG1tZXPP/+cadOmUVlZyU9+8hPy8vJczlPFWUREegxPt4wMCgoiKCiIkSNHAnDnnXfy+eefu5ynirOIiPQYY8aMYfny5ezZswfgrC0jTzrZMvKZZ57hyiuvpKGhge3btztbRk6YMIFNmzZxzTXXMHDgQL788ksAPvjgAyIiIlzOU7O1RUSkxzi1ZaSfnx/R0dEEBwc73z/ZMvK6664jMjKSAwcOACdaRm7btg3DMBgzZgxRUVHk5eXx2muvYTabueaaa5x9mxcuXEhaWhpHjx5l8ODB/PnPf3Y5T60Q1glaIUxExHVaIaxjHX0WunLuhM079hOcudqrOdT2nuTV+ACRg671avzlc93rRuauNfEvdms8dzXvfb5b490z6LFui/VS7w+6Ldao0a92W6zOSDMVeTsFdt5i93YKPZa+cxYREfExKs4iIiI+xmPFeefOnaSmphISEkJERATjxo2jurqa2tparFYrABUVFcyYMcPtGLm5uS6PycrKwmazYbfbue222/j666/dji8iIuIJHinOhmGQnJxMfHw8NTU1bN26ldzcXHbt2tVmv9jYWBYsWOB2HHeK88yZM9m0aRMOh4Px48fzzDPPuB1fRETEEzxSnMvKyjCbzUydOtW5zW63M2pU2wXJ165dy/jx4wFoampiypQpxMXFER0dzapVqwAoLCwkJSWFsWPHEhoayqxZswDIzMzk8OHD2O120tLSOp3bJZdc4vy5qampw4fRRUREvMEjs7WrqqqIiYlxaUxOTg4JCQksXryYffv2MWLECG699VYAHA4HlZWV+Pv7ExYWxvTp08nLy2PRokU4HA6X8/vP//xPXnnlFQICAigrKzvjPvn5+eTn5wNw7NB+l2OIiMj5ITs7m759+/Loo4+e87G+/PJL7rnnHufr7du388wzz/DQQw+5dByfeZSqpKSE4uJiZ0/N5uZm6uvrgRMrugQE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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/cifar10_balance_iid_100clients.csv\"\n",
    "partition_report(trainset.targets, balance_iid_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "balance_iid_part_df = pd.read_csv(csv_file,header=1)\n",
    "balance_iid_part_df = balance_iid_part_df.set_index('client')\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "for col in col_names:\n",
    "    balance_iid_part_df[col] = (balance_iid_part_df[col] * balance_iid_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "balance_iid_part_df[col_names].iloc[:10].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/cifar10_balance_iid_100clients.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unbalanced IID"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "100\n"
     ]
    }
   ],
   "source": [
    "unbalance_iid_part = CIFAR10Partitioner(trainset.targets, \n",
    "                                num_clients,\n",
    "                                balance=False, \n",
    "                                partition=\"iid\",\n",
    "                                unbalance_sgm=0.3,\n",
    "                                seed=seed)\n",
    "# # save to pkl file\n",
    "# torch.save(unbalance_iid_part.client_dict, \"cifar10_unbalance_iid.pkl\")\n",
    "print(len(unbalance_iid_part))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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PDCriFx6Q4YMISsri4cffphrr732gqVI+/TpA3xbinTQoEH885//pL6+nsOHDztKke7atYvNmzczY8YMx9qydevWMWvWLJcTODiZxB988EGee+45UlJSCAwMdHkSERERf+LtUqSe4lQp0kGDBrFhwwaPTepv2iuTJyIinqFSpJ3r7Gfh1J34nXfeybRp08jOzm6zZ7t///6eiVBERERc5lQSP3sXfu5vSgEBAaxd69nN9yIiIuI8p5J4ZWWlt+MQERERFzl1Ytv+/ft58MEH+fnPfw7Atm3bePnll70amIiIiHTOqSQ+fvx4MjIy+PzzzwG47bbbOj19TURERLzPqST+1VdfMWbMGK644kzzbt26aauZiIhIF3PqO/Ef/OAHHDx40HF6zUcffURoaKhXAxMREfElhYWF9OzZk8cee8wj40VGRnLVVVcRGBhIt27d3NrK7FQS/8tf/sLIkSOpra3lJz/5CQcOHHC51KiIiIi7/nyve/W2O/Lo4s4LbF0qlZWVjvPZ3eFUEu/fvz/r169n+/btGIZBTEyMzlIXEZHL2iuvvMKsWbMICAjAZDIRFRXleK+0tJSSkhJOnjzJrbfeyquvvsqVV17Jm2++yR//+EcCAwMJDQ3lvffeY+vWrTzwwAOcPHmS06dPU1ZWRnR0tEdi7DSJr127FovFwrJly9pc37FjB4CjQsvlbsvew0Tmr7qkc9aFjPPYWIk33+ixsc61ZEarV8Y939r0F7w6fvOhv3h0vHtv/p1HxzvfSyFrvDp+2uCLq/SXE1DmoUics+9O8yWdT74fLkUp0oCAAO666y4CAgL4n//5H/Ly8lyOs9Mkvn79eiwWC//85z+/815AQMD3JomLiMj3i7dLkQK8//77hIeH8+WXXzJs2DD69evH4MGDXYqz0yT+xz/+EYC///3vLg0qIiLiz7xditRisRAeHg5Ar169yMrKYuPGjZ5N4n/5S+ePGR955JEO39u3bx9Tp06lqqqK4OBgIiMjmT17Nt27dyczM5OamhpsNhuvvPJKm0cUriguLuaJJ55wq++sWbOYNm0aBw4cuKhFBSIicvnxdinSQYMGcfr0aa666iqampqoqKhwq7pZp0n8bGm19nT2G4phGGRlZZGbm8uiRYsAsNvt7N+/n759+zrapaamkpqa6mrMDu4m8fr6elavXs2NN3rnu2IREfFv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cOBAhg4dCoDdbqe6uprg4GBiYmKYPHkyVquVefPmOR5HuOL3v/89r7zyCqGhoVRWVrrcX0RELm/OlCJ98sknaWxs5JtvviEjIwP4thTpmDFjyM7OBs6UIi0qKqKhoYHs7Gyio6Md45w8eZLy8nJmzJjhVpw+s7CtoqICq9WK2WwmPT2d5uZm9uzZA5ypJhMaGkpISAhxcXHs3r37ouYqKiqivr6enJwc5s2b126bkpISUlNTSU1N5dSxwxc1n4iI+BdvlyI96+2336Z///707t3brTi9ksTj4+P5+OOPXepjGAZlZWXY7Xbsdjt79uxxHPgeHBzsaBcYGEhrq/v7n881btw4ysra3+ebl5eHzWbDZrMReGWoR+YTERH/4O1SpGe98cYbbj9KBy8lcYvFwokTJygtLXVcq6qqYv369R32ycjIYO7cuZwtqlZdXX3BeYKCgmhpaXEptnMXFJSXl9OvXz+X+ouIyOXv3FKkSUlJPPLII23eP1uKdNiwYW3yyLRp00hMTCQhIYHBgweTlJTE4sWLSUhIwGw285///If7778fgGPHjrF69WrHY3d3eOU78YCAAJYvX87UqVOxWq2EhIQ4tph1pKCggKlTp2IymTAMg8jISFauXNnpPHl5eZhMJvr379/mN6HO5Ofns337dq644gpuuukmXnzxRVc+moiIdIGu2BKWm5tLbm5uu+9NmjSJSZMmfef6smXLvnPt8ccf5/HHH//O9SuvvNJxp+8u1RN3QnBYNGG5s13qo2NXv6VjV52jY1fl+0r1xDuneuIiIiKXIRVAcUJin1Bs1rtd7HXxK9q3XOwA7T8Fcopnf/+1eHS0zhSSduFGnfR21b6LmE1E5GLpTlxERMRPKYmLiIj4KSVxERERP6UkLiIi4gRPliIFeO6554iPjychIYH77rvPceqbK7SwTUREfN6atVEeHW+Ipdaj47lq7969zJkzh23bttGjRw/GjBnDokWLGD9+vEvj6E5cRESkHd4uRdra2srx48dpbW3l2LFjhIeHuxyjkriIiMh5vF2KtE+fPjz22GPceOONhIWFERoayl133eVynEriIiIi53GmFGlaWhqJiYm8/vrrbN26Ffi2FGlpaSmnTp0CzpQiLS4uZubMmezevZsePXpw6NAhVqxYwWeffcbnn39OU1MTr732mstxKomLiIicx9ulSN99911uvvlmrr/+eoKCgsjOzuaDDz5wOU4lcRERkfN4uxTpjTfeyEcffcSxY8cwDIM1a9a4dVa8VqeLiIic59xSpIGBgSQnJxMZGel4/2wp0ptuuonExESOHj0KnClFunPnTgzDYMiQISQlJWG1WnnttdcICgrihhtu4A9/+APXXHMNo0ePpn///nTr1o3k5GTy8vJcjlNVzJzgThWzzniiwhlcXJUzdyqceaKymasVzc66mMpm57qYKmfnc6fqmTM8URlNFdHEn6iKWedUxUxEROQypCQuIiLip7yWxPft28fYsWOJiooiLi6OESNGsGPHDurq6khISADAZrMxZcoUt+coLi52uc+0adPo168fJpOJrKwsGhsb3Z5fRESkK3kliRuGQVZWFunp6dTW1rJt2zaKi4vZv39/m3apqanMmTPH7XncSeLDhg2jpqaGzZs3c9tttzFjxgy35xcREelKXknilZWVBAUFMXHiRMc1s9lMWlpam3br1q0jMzMTgKamJiZMmMCAAQNITk5mxYoVACxYsIDs7GyGDx9OdHQ006dPByA/P5/jx49jNpvJyclxOra77rqLbt3OLMq//fbbaWhouKjPKiIi0lW8ssWspqaGlJQUl/oUFRVhsViYP38+jY2NDBw4kKFDhwJgt9uprq4mODiYmJgYJk+ejNVqZd68edjtdrfjnD9/Pvfee2+775WUlFBSUgLAqWOH3Z5DRETEW3xmYVtFRQVWqxWz2Ux6ejrNzc3s2bMHOLPpPjQ0lJCQEOLi4ti9e/dFz1dUVES3bt06vIvPy8vDZrNhs9kIvDL0oucTERH/5ulSpM8//zwJCQnEx8cze/Zst8bwyp14fHw8S5cudamPYRiUlZURExPT5vqGDRsIDg52vA4MDKS11fU9zudauHAhK1euZM2aNZ0eqyciIr7hhkq7R8fr6rMUampqKC0tZePGjXTv3p3hw4dz9913Ex0d7dI4XrkTt1gsnDhxgtLSUse1qqoq1q9f32GfjIwM5s6dy9mzZ6qrqy84T1BQEC0tLS7F9s477zBz5kzKy8u58sorXeorIiLfH94sRfrpp59y++23c+WVV9KtWzd+9rOfsXz5cpdj9EoSDwgIYPny5axevZqoqCji4+MpLCzstFZqQUEBLS0tmEwmEhISKCgouOA8eXl5mEwmlxa2/eY3v+Ho0aMMGzYM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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/cifar10_unbalance_iid_unbalance_sgm_0.3_100clients.csv\"\n",
    "partition_report(trainset.targets, unbalance_iid_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "unbalance_iid_part_df = pd.read_csv(csv_file,header=1)\n",
    "unbalance_iid_part_df = unbalance_iid_part_df.set_index('client')\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "for col in col_names:\n",
    "    unbalance_iid_part_df[col] = (unbalance_iid_part_df[col] * unbalance_iid_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "unbalance_iid_part_df[col_names].iloc[:10].plot.barh(stacked=True)  \n",
    "plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "# plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/cifar10_unbalance_iid_unbalance_sgm_0.3_100clients.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYYAAAEHCAYAAACqbOGYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAVl0lEQVR4nO3df1TV9R3H8RfCtrPtoFsLGMoUCRTkt8Is6qjoIKf5C6wjYWJklLkta+hc5+znmcrph5pjtZHOMddc5alsWZoz6yTYEJPtmNlhCZso40egoEny47M/nHd+EhjS/cGl5+OczoEv936/74+d49P7/XLv18cYYwQAwH8N8fQAAICBhTAAACyEAQBgIQwAAAthAABY/Dw9QF9ce+21Cg0N9fQYAOBVqqur1djYeNXP84owhIaGqry83NNjAIBXSUpK6tfzOJUEALAQBgCAhTAAACyEAQBgIQwAAAthAABYCAMAwEIYAAAWwgAAsHjFO58/Sx56ot7p+1xzX6DT97vmvkCn7g/AwMErBgCAhTAAACyEAQBgIQwAAAthAABYCAMAwEIYAAAWwgAAsBAGAICFMAAALIQBAGAhDAAAC2EAAFgIAwDA4rIwnDhxQqmpqYqKilJ0dLQef/xxSVJTU5PS0tIUERGhtLQ0NTc3u2oEAEA/uCwMfn5+euyxx/Tee+/p7bff1q9+9SsdPXpUBQUFmjZtmiorKzVt2jQVFBS4agQAQD+4LAzBwcEaP368JMnf319RUVE6efKkduzYoZycHElSTk6OXnzxRVeNAADoB7dcY6iurtbhw4c1ceJE1dXVKTg4WNLFeNTXO/+OZQCA/nP5rT3Pnj2rzMxMbdiwQUOHDu3z84qKilRUVCRJamhocNV4AIBPcOkrhvb2dmVmZio7O1sZGRmSpKCgINXW1kqSamtrFRjY/b2D8/LyVF5ervLycgUEBLhyTADAZVwWBmOM7rrrLkVFRenBBx90bJ89e7aKi4slScXFxZozZ46rRgAA9IPLTiWVlJRo69atio2NVUJCgiRpzZo1WrVqlW677TZt3rxZI0eO1HPPPeeqEQAA/eCyMNx0000yxnT7s71797rqsACAT4l3PgMALIQBAGAhDAAAC2EAAFgIAwDAQhgAABbCAACwEAYAgIUwAAAshAEAYCEMAAALYQAAWAgDAMBCGAAAFsIAALAQBgCAhTAAACyEAQBgIQwAAAthAABYCAMAwEIYAAAWwgAAsPh5egBv9dAT9U7f55r7Ap2+T1fizwAYnHjFAACwEAYAgIUwAAAshAEAYCEMAAALYQAAWAgDAMBCGAAAFsIAALAQBgCAhTAAACyEAQBgIQwAAAthAABYXBaG3NxcBQYGKiYmxrHtpz/9qUaMGKGEhAQlJCTolVdecdXhAQD95LIwLF68WLt27bpi+wMPPKCKigpVVFRoxowZrjo8AKCfXBaGSZMm6ZprrnHV7gEALuL2awyFhYWKi4tTbm6umpub3X14AMD/4dYwLF26VB988IEqKioUHBys73//+z0+tqioSElJSUpKSlJDQ4MbpwSAzza3hiEoKEi+vr4aMmSI7r77bpWVlfX42Ly8PJWXl6u8vFwBAQFunBIAPtvcGoba2lrH1y+88IL1G0sAgIHBz1U7zsrK0htvvKHGxkaFhIToZz/7md544w1VVFTIx8dHoaGh+s1vfuOqwwMA+sllYdi2bdsV2+666y5XHQ4A4CS88xkAYCEMAAALYQAAWAgDAMBCGAAAFsIAALAQBgCAhTAAACyEAQBgIQwAAAthAABYCAMAwEIYAAAWwgAAsBAGAIClT2EoKSnp0zYAgPfrUxi++93v9mkbAMD79XoHtwMHDqi0tFQNDQ1at26dY3tLS4s6OztdPhwAwP16DcOFCxd09uxZdXR0qLW11bF96NCh2r59u8uHAwC4X69hmDx5siZPnqzFixdr1KhR7poJAOBBvYbhko8//lh5eXmqrq5WR0eHY/vrr7/ussEAAJ7RpzDceuutuvfee7VkyRL5+vq6eiYAgAf1KQx+fn5aunSpq2cBAAwAffp11VmzZumJJ55QbW2tmpqaHP8BAAafPr1iKC4uliQ98sgjjm0+Pj46fvy4a6YCAHhMn8JQVVXl6jkAAANEn8Lw+9//vtvtixYtcuowAADP61MYDh486Pi6ra1Ne/fu1fjx4wkDAAxCfQrDL3/5S+v7M2fO6I477nDJQAAAz+rXx25/6UtfUmVlpbNnAQAMAH16xTBr1iz5+PhIkjo7O/Xee+/ptttuc+lgAADP6FMY8vPz//cEPz+NGjVKISEhLhsKAOA5fQrD5MmTVVdX57gIHRER4dKh8Nn20BP1Tt/nmvsCnb5PYLDq0zWGZ599Vt/85jf13HPP6dlnn9XEiRP52G0AGKT69Iph9erVOnjwoAIDL/6rq6GhQd/61rc0f/58lw4HAHC/Pr1i6OrqckRBkr72ta+pq6vLZUMBADynT68Ypk+frptvvllZWVmSpGeeeUYzZsxw6WAAAM/oNQz/+Mc/VFdXp0ceeUTPP/+89u/fL2OMbrjhBmVnZ7trRgCAG/V6Kmn58uXy9/eXJGVkZGjdunVav369ZsyYoeXLl7tjPgCAm/UahurqasXFxV2xPSkpSdXV1b3uODc3V4GBgYqJiXFsa2pqUlpamiIiIpSWlqbm5ub+TQ0AcJlew9DW1tbjz86fP9/rjhcvXqxdu3ZZ2woKCjRt2jRVVlZq2rRpKigouIpRAQDu0GsYkpOT9dRTT12xffPmzZowYUKvO540aZKuueYaa9uOHTuUk5MjScrJydGLL754leMCAFyt14vPGzZs0Lx58/T00087QlBeXq4LFy7ohRdeuOqD1dXVKTg4WJIUHBys+vqe3+FaVFSkoqIiSRffNwEAcI9ewxAUFKTS0lLt27dPR44ckSTNnDlTU6dOdflgeXl5ysvLk3TxmgYAwD369D6G1NRUpaamfuqDBQUFqba2VsHBwaqtrbXeNAcAGBj6dT+G/po9e7aKi4slScXFxZozZ447Dw8A6AOXhSErK0s33HCD3n//fYWEhGjz5s1atWqV9uzZo4iICO3Zs0erVq1y1eEBAP3Up1NJ/bFt27Zut+/du9dVhwQAOIFbTyUBAAY+wgAAsBAGAICFMAAALIQBAGAhDAAAC2EAAFgIAwDAQhgAABbCAACwEAYAgIUwAAAshAEAYCEMAAALYQAAWAgDAMBCGAAAFsIAALAQBgCAhTAAACyEAQBgIQwAAAthAABYCAMAwEIYAAAWwgAAsBAGAICFMAAALIQBAGAhDAAAC2EAAFgIAwDAQhgAABbCAACwEAYAgIUwAAAshAEAYCEMAACLnycOGhoaKn9/f/n6+srPz0/l5eWeGAMA0A2PhEGS9u3bp2uvvdZThwcA9IBTSQAAi0deMfj4+Cg9PV0+Pj665557lJeXd8VjioqKVFRUJElqaGhw94gYhB56ot7p+1xzX6DT9wl4mkfCUFJSouHDh6u+vl5paWmKjIzUpEmTrMfk5eU5gpGUlOSJMQHgM8kjp5KGDx8uSQoMDNS8efNUVlbmiTEAAN1wexjOnTun1tZWx9evvfaaYmJi3D0GAKAHbj+VVFdXp3nz5kmSOjo6dPvtt2v69OnuHgMA0AO3hyEsLEx/+9vf3H1YAEAf8euqAAALYQAAWAgDAMBCGAAAFsIAALAQBgCAhTAAACyEAQBgIQwAAAthAABYCAMAwEIYAAAWwgAAsHjkDm7uxO0cAeDq8IoBAGAhDAAAC2EAAFgIAwDAQhgAABbCAACwEAYAgIUwAAAshAEAYCEMAAALYQAAWAgDAMBCGAAAFsIAALAQBgCAhTAAACyEAQBgGfR3cAO8lbPvPnjpzoOu2i8Gzx0jecUAALAQBgCAhTAAACyEAQBgIQwAAAthAABYPBKGXbt2aezYsQoPD1dBQYEnRgAA9MDtYejs7NSyZcv06quv6ujRo9q2bZuOHj3q7jEAAD1wexjKysoUHh6usLAwff7zn9eCBQu0Y8cOd48BAOiBjzHGuPOA27dv165du7Rp0yZJ0tatW/XXv/5VhYWF1uOKiopUVFQkSTp27JgiIyP7fcyGhgYFBAT0f+gBhvUMfINtTaxn4OtuTdXV1WpsbLzqfbn9IzG665CPj88V2/Ly8pSXl+eUYyYlJam8vNwp+xoIWM/AN9jWxHoGPmeuye2nkkJCQnTixAnH9zU1NRo+fLi7xwAA9MDtYUhOTlZlZaWqqqp04cIF/elPf9Ls2bPdPQYAoAduP5Xk5+enwsJC3Xzzzers7FRubq6io6NdekxnnZIaKFjPwDfY1sR6Bj5nrsntF58BAAMb73wGAFgIAwDA4vVhOHHihFJTUxUVFaXo6Gg9/vjjkqSmpialpaUpIiJCaWlpam5udjxn7dq1Cg8P19ixY7V7925Pjd6rzs5OJSYm6pZbbpHk/es5ffq05s+fr8jISEVFRenAgQNevab169crOjpaMTExysrKUltbm1etJzc3V4GBgYqJiXFs68/8hw4dUmxsrMLDw/W9732v219Hd5fu1rRixQpFRkYqLi5O8+bN0+nTpx0/G+hr6m49lzz66KPy8fGx3qPg1PUYL3fq1Clz6NAhY4wxLS0tJiIiwrz77rtmxYoVZu3atcYYY9auXWtWrlxpjDHm3XffNXFxcaatrc0cP37chIWFmY6ODo/N35PHHnvMZGVlmZkzZxpjjNevZ9GiReapp54yxhjz8ccfm+bmZq9dU01NjQkNDTUfffSRMcaYW2+91WzZssWr1vPmm2+aQ4cOmejoaMe2/syfnJxsSktLTVdXl5k+fbp55ZVX3L+Y/+puTbt37zbt7e3GGGNWrlzpVWvqbj3GGPOvf/3LpKenm5EjR5qGhgZjjPPX4/Vh+KTZs2eb1157zYwZM8acOnXKGHMxHmPGjDHGGLNmzRqzZs0ax+PT09NNaWmpR2btyYkTJ8zUqVPN3r17HWHw5vWcOXPGhIaGmq6uLmu7t66ppqbGhISEmA8//NC0t7ebmTNnmt27d3vdeqqqqqy/dK52/lOnTpmxY8c6tv/xj380eXl5bpq+e59c0+Wef/55c/vttxtjvGdN3a0nMzPTVFRUmFGjRjnC4Oz1eP2ppMtVV1fr8OHDmjhxourq6hQcHCxJCg4OVn39xZt0nzx5Ut/4xjcczwkJCdHJkyc9Mm9Pli9frocfflhDhvzvf483r+f48eMKCAjQnXfeqcTERC1ZskTnzp3z2jWNGDFC+fn5GjlypIKDgzVs2DClp6d77Xouudr5T548qZCQkCu2D1S//e1v9e1vf1uS967ppZde0ogRIxQfH29td/Z6Bk0Yzp49q8zMTG3YsEFDhw7t8XGmjx/J4Skvv/yyAgMDNWHChD49fqCvR5I6Ojr0zjvvaOnSpTp8+LC+/OUv9/px6wN9Tc3NzdqxY4eqqqp06tQpnTt3Tn/4wx96fPxAX8//09P83rSu1atXy8/PT9nZ2ZK8c00fffSRVq9erZ///OdX/MzZ6xkUYWhvb1dmZqays7OVkZEhSQoKClJtba0kqba2VoGBgZIG/kdylJSU6KWXXlJoaKgWLFig119/XQsXLvTa9UgXZwwJCdHEiRMlSfPnz9c777zjtWv6y1/+otGjRysgIECf+9znlJGRodLSUq9dzyVXO39ISIhqamqu2D7QFBcX6+WXX9bTTz/t+EvRG9f0wQcfqKqqSvHx8QoNDVVNTY3Gjx+vf//7385fz6c6ATYAdHV1mTvuuMPcf//91vb8/HzrQtqKFSuMMcYcOXLEukgzevRoj18I7Mm+ffsc1xi8fT033XSTOXbsmDHGmJ/85CcmPz/fa9f09ttvm3Hjxplz586Zrq4us2jRIrNx40avW88nz1/3Z/6kpCRz4MABx4XNnTt3un8hl/nkml599VUTFRVl6uvrrcd5y5p6u2Zy+TUGZ6/H68Pw1ltvGUkmNjbWxMfHm/j4eLNz507T2Nhopk6dasLDw83UqVPNhx9+6HjOL37xCxMWFmbGjBnj0d+i+H8uD4O3r+fw4cNmwoQJJjY21syZM8c0NTV59Zp+/OMfm7Fjx5ro6GizcOFC09bW5lXrWbBggfn6179u/Pz8zIgRI8ymTZv6Nf/BgwdNdHS0CQsLM8uWLbviFwzcqbs1XXfddSYkJMTxd8M999zjePxAX1N367nc5WEwxrnr4SMxAACWQXGNAQDgPIQBAGAhDAAAC2EAAFgIAwDAQhgAABbCAAwwv/vd7/Sd73zH02PgM4wwAAAshAFeq7q6WlFRUbr77rsVHR2t9PR0nT9/XlOmTFF5ebkkqbGxUaGhoZIu/kt87ty5mjVrlkaPHq3CwkKtW7dOiYmJuv7669XU1NTjsTZu3Khx48YpLi5OCxYskCSVlZUpJSVFiYmJSklJ0fvvv39Vx5kyZYqWL1+ulJQUxcTEqKys7IrjNjQ0KDMzU8nJyUpOTlZJSYkk6c0331RCQoISEhKUmJio1tZWp/25Al7/kRj47KqqqjK+vr7m8OHDxpiLN8zZunWrmTx5sjl48KAxxpiGhgYzatQoY4wxW7ZsMdddd51paWkx9fX1ZujQoebJJ580xhizfPlys379+h6PFRwcbNra2owxxjQ3NxtjLt5n4tJNYPbs2WMyMjKu6jiTJ082S5YsMcZcvCnLpc/E2bJli1m2bJkxxpisrCzz1ltvGWOM+ec//2kiIyONMcbccsstZv/+/cYYY1pbWx1zAM7g5+kwAZ/G6NGjlZCQIEmaMGGCqqure318amqq/P395e/vr2HDhmnWrFmSpNjYWP3973/v8XlxcXHKzs7W3LlzNXfuXEnSmTNnlJOTo8rKSvn4+Ki9vf2qj5OVlSVJmjRpklpaWqxbT0oXP8n16NGjju9bWlrU2tqqG2+8UQ8++KDjE4Uv/8x94NPiVBK82he+8AXH176+vuro6JCfn5+6urokSW1tbT0+fsiQIY7vhwwZoo6Ojh6Ps3PnTi1btkyHDh3ShAkT1NHRoR/96EdKTU3VkSNH9Oc//9k6Vl+P88nPxv/k911dXTpw4IAqKipUUVGhkydPyt/fX6tWrdKmTZt0/vx5XX/99Tp27Fjvf1DAVSAMGHRCQ0N16NAhSdL27ds/9f66urp04sQJpaam6uGHH9bp06d19uxZnTlzRiNGjJB08bpCfzzzzDOSpP3792vYsGEaNmyY9fP09HQVFhY6vq+oqJB08bP5Y2Nj9YMf/EBJSUmEAU5FGDDo5Ofn68knn1RKSooaGxs/9f46Ozu1cOFCxcbGKjExUQ888IC+8pWvaOXKlfrhD3+oG2+8UZ2dnf3a91e/+lWlpKTo3nvv1ebNm6/4+caNG1VeXq64uDiNGzdOv/71ryVJGzZsUExMjOLj4/XFL37RcctKwBn42G3AQ6ZMmaJHH31USUlJnh4FsPCKAQBg4RUDcJlly5Y53itwyf33368777zTQxMB7kcYAAAWTiUBACyEAQBgIQwAAAthAABY/gOO7IeCBjCfEwAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# plot sample number distribution for clients\n",
    "clt_sample_num_df = unbalance_iid_part.client_sample_count\n",
    "sns.histplot(data=clt_sample_num_df, \n",
    "             x=\"num_samples\", \n",
    "             edgecolor='none', \n",
    "             alpha=0.7, \n",
    "             shrink=0.95,\n",
    "             color=hist_color)\n",
    "plt.savefig(f\"../imgs/cifar10_unbalance_iid_unbalance_sgm_0.3_100clients_dist.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Balanced Dirichlet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "balance_dir_part = CIFAR10Partitioner(trainset.targets, \n",
    "                                num_clients,\n",
    "                                balance=True, \n",
    "                                partition=\"dirichlet\",\n",
    "                                dir_alpha=0.3,\n",
    "                                seed=seed)\n",
    "# # save to pkl file\n",
    "# torch.save(balance_dir_part.client_dict, \"cifar10_balance_dir.pkl\")\n",
    "print(len(balance_dir_part))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/cifar10_balance_dir_alpha_0.3_100clients.csv\"\n",
    "partition_report(trainset.targets, balance_dir_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "balance_dir_part_df = pd.read_csv(csv_file,header=1)\n",
    "balance_dir_part_df = balance_dir_part_df.set_index('client')\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "for col in col_names:\n",
    "    balance_dir_part_df[col] = (balance_dir_part_df[col] * balance_dir_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "balance_dir_part_df[col_names].iloc[:10].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/cifar10_balance_dir_alpha_0.3_100clients.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unbalanced Dirichlet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "unbalance_dir_part = CIFAR10Partitioner(trainset.targets, \n",
    "                                num_clients,\n",
    "                                balance=False, \n",
    "                                partition=\"dirichlet\",\n",
    "                                unbalance_sgm=0.3,\n",
    "                                dir_alpha=0.3,\n",
    "                                seed=seed)\n",
    "# # save to pkl file\n",
    "# torch.save(unbalance_dir_part.client_dict, \"cifar10_unbalance_dir.pkl\")\n",
    "print(len(unbalance_dir_part))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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uWv1YzxjxSRAGkPP3+g8IzhrbztnFBH3cHfJyI7SppKRQ4YMYePGjR026cVGJSNFRJynkpGta+27aNOZ85133snMmTNJSEhotnJ60KBBHROhiIiIOLQpOTedNZ/5G5CXlxfr1nXsw+MiIiLSxuRcWFjo7jhERETk/9emHcL27dvHww8/zC9+8QsAduzYwWuvvebWwERERLqqNiXnyZMnExcXxzfffAPAgAEDWn1mWURERFzXpuT8/fffc99993HJJaebd+vWrUs+UiUiInIhtOme889+9jMOHDjg2FXliy++wNdXxSBEROTil5aWRs+ePXnyySc7ZLyAgAAuv/xyvL296datm0uP4rYpOb/88suMHTuW8vJybrvtNr777junC1uIiIj8u9/f37HFh55Y3vq2zxdKYWGhY/9uV7QpOQ8aNIgNGzawc+dODMMgODjYqb22RUREPMXrr7/OSy+9hJeXF1arlcDAQMd72dnZZGVlceLECW6++WbeeOMNLrvsMt555x1mz56Nt7c3vr6+fPzxx2zfvp2HHnqIEydOcOrUKXJzcwkKCuqQGFtNzuvWrSM2NpZ333232fGysjLgdGmtrmDbnsMEpKxx+zwV3Se5dfywfje6ZdwVc10v4Xku62IWd+h4Z6o7+LLbxga4v99Tbhn31e5rO2ScocM6fm/5RK/cDh/z3+290+b2OaRruBAlI728vBg5ciReXl78+te/Jjk52ek4W03OGzZsIDY2lr/97W9nvefl5dVlkrOIiPw0tKVk5LPPPsuhQ4c4evQocXFxwL9KRt53332O3HfrrbeSnp5OTU0NCQkJjrPmTz/9FD8/P/bv38+IESMYOHAgw4YNcyrOVldrz549G4C//OUvZ/1ZsmRJqwPv3buXCRMmEBgYSGhoKKNHj6asrIyKigpHDeXi4mJmzJjhVMBnysjIcLlv0yWN77//3uUxRETk4tKWkpGLFi1i27ZtPP/889TV1QGnz5LnzJlDdXU1NpuNAwcOMGnSJPLy8ujRowdxcXGOXTP9/PwA6NWrF/Hx8WzatMnpOFs9c3755dYvAf72t78953HDMIiPjycpKYlly5YBYLfb2bdvH3379nW0i4qKIioqytmYHTIyMnjmmWec7lddXU1BQQE33uiey7wiIuKZ3F0ycsiQIZw6dYrLL7+c2tpa8vPzXapW1eqZ85Ej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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# generate partition report\n",
    "csv_file = \"../partition-reports/cifar10_unbalance_dir_alpha_0.3_unbalance_sgm_0.3_100clients.csv\"\n",
    "partition_report(trainset.targets, unbalance_dir_part.client_dict, \n",
    "                 class_num=num_classes, \n",
    "                 verbose=False, file=csv_file)\n",
    "\n",
    "unbalance_dir_part_df = pd.read_csv(csv_file,header=1)\n",
    "unbalance_dir_part_df = unbalance_dir_part_df.set_index('client')\n",
    "col_names = [f\"class{i}\" for i in range(num_classes)]\n",
    "for col in col_names:\n",
    "    unbalance_dir_part_df[col] = (unbalance_dir_part_df[col] * unbalance_dir_part_df['Amount']).astype(int)\n",
    "\n",
    "# select first 10 clients for bar plot\n",
    "unbalance_dir_part_df[col_names].iloc[:10].plot.barh(stacked=True)  \n",
    "# plt.tight_layout()\n",
    "plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
    "plt.xlabel('sample num')\n",
    "plt.savefig(f\"../imgs/cifar10_unbalance_dir_alpha_0.3_unbalance_sgm_0.3_100clients.png\", dpi=400, bbox_inches = 'tight')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# plot sample number distribution for clients\n",
    "clt_sample_num_df = unbalance_dir_part.client_sample_count\n",
    "sns.histplot(data=clt_sample_num_df, \n",
    "             x=\"num_samples\", \n",
    "             edgecolor='none', \n",
    "             alpha=0.7, \n",
    "             shrink=0.95,\n",
    "             color=hist_color)\n",
    "plt.savefig(f\"../imgs/cifar10_unbalance_dir_alpha_0.3_unbalance_sgm_0.3_100clients_dist.png\", dpi=400, bbox_inches = 'tight')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.4 ('fedlab')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "232.713px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "vscode": {
   "interpreter": {
    "hash": "3ba7c791aa8d51007ff2c2ccf5bd0ffd8f40e8d3627fc5a38f863bdac0d8711e"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
